#include "disassemble.hpp"
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

using namespace std;

typedef struct Instruction {
    char instr[30];
    int params[5];
    int length;

} Instruction;


int hexToInt(char);
void setup();
void setFlags(int);
char * dissemble(int bytes);
int setFile(char *);
Instruction * nextInstruction();
int isInArray(int,int*,int);

char codes[256][30];
int flags = 0;
FILE * file;


int main() {
    int f = setFile("/home/william/test.o65");
    if (f == -1)
        printf("file not found");
    setup();
    setFlags(32+16);
    Instruction * instr;
    while((instr = nextInstruction()) != NULL) {

        printf("%s %d: ", instr->instr, instr->length);
        int i;
        for (i = 0; i < instr->length; i++) {
            printf("%d ",instr->params[i]);
        }
        printf("\n");
        free(instr);
    }
    if (file != NULL)
        fclose(file);
    return 0;
}

int setFile(char * filename) {
    if (file != NULL)
        fclose(file);
    file = fopen(filename,"rb");
    if (file == NULL)
        return -1;
    return 0;
}

Instruction * nextInstruction() {
    int op = fgetc(file);
    int x= (flags & 0x10)>>4;
    int m = (flags & 0x20)>>5;
    int e = (flags & 0x100)>>8;
    Instruction * instr = (Instruction*)malloc(sizeof(Instruction));
    if (op != EOF) {
        strcpy(instr->instr,dissemble(op));
        char * params = &instr->instr[3];
        int i = 0;
        int bytes = 0;
        while (params[i] != '\0') {
            if (params[i] == 'd' && params[i+1] == 'p') { //dp
                bytes += DPBYTES;
                i+=2;
            } else if (params[i] == 's' && params[i+1] == 'r') {//sr
                bytes += SRBYTES;
                i+=2;
            } else if (params[i] == 'a' && params[i+1] == 'd') {//addr
                bytes += ADDRBYTES;
                i+=4;
            } else if (params[i] == 'l' && params[i+1] == 'o') {//long
                bytes += LONGBYTES;
                i+=4;
            } else if (params[i] == 'n' && params[i+1] == 'e') {//nearlabel
                bytes += NEARLABELBYTES;
                i+=9;
            } else if (params[i] == 'l' && params[i+1] == 'a') {//label
                bytes += LABELBYTES;
                i+=5;
            } else if (params[i] == '#') { //#const
                bytes += CONSTBYTES;
                if (isInArray(op,constAExceptions,constAExceptionNum)) {
                    if (m == 0 && e ==0)
                        bytes += 1;

                } else if (isInArray(op,constXYExceptions,constXYExceptionNum)) {
                    if (x == 0 && e = 0)
                        bytes += 1;
                }
                i+=5;
            } else {
                i++;
            }

        }
        int k;
        for (k = 0; k < bytes; k++) {
            instr->params[k] = fgetc(file);
        }
        instr->length = bytes;

    } else {
        if (file != NULL)
            fclose(file);
        file = NULL;
        return NULL;

    }
    return instr;
}
int isInArray(int c, int * array, int length) {
    int i;
    for (i = 0; i < length; i++) {
        if (array[i] == c)
            return 1;
    }
    return 0;
}

void setFlags(int flagparam) {
    flags = flagparam;
}

char* dissemble(int bytes) {
    return codes[bytes];
}

void setup()
{
    FILE * keys = fopen("/home/william/65816opcodes","r");
    if (keys == NULL) {
        printf("file cannot be opened");
        return;
    }
    char assembly[30];
    while (fgets(assembly,sizeof assembly, keys)!=NULL) {
        int i = 0;
        while(assembly[i] != '\0') {
            if (assembly[i] == '\n')
                assembly[i] = '\0';
            i++;
        }
        if (hexToInt(assembly[0]) != -1) {
            int opcode = hexToInt(assembly[0])*16 + hexToInt(assembly[1]);
            strcpy(codes[opcode],&assembly[3]);
            //printf("%d: %s",opcode,codes[opcode]);
            //int c = getchar();
        }
    }

    fclose(keys);
    return;
}

int hexToInt(char digit) {
    if (digit >= 48 && digit <= 57 )
        return digit-48;
    else if (digit >= 65 && digit <= 70)
        return digit-55;
    else
        return -1;
}
